use std::{fmt::Debug};
use log::{info};
use winapi::um::winnt::VOID;
use std::os::raw::{c_char, c_void};

#[allow(non_snake_case, dead_code, non_camel_case_types)]
#[derive(Debug)]
pub struct CamConfig {
    pub cam_type: i32,
    pub Fov: f32,
    pub Near: f32,
    pub Pos_x: f32,
    pub Pos_y: f32,
    pub Pos_z: f32,
    // pub Tgt_x: f32,
    // pub Tgt_y: f32,
    // pub Tgt_z: f32,
}

type SetCameraFunc = unsafe extern "C" fn(a: *const c_void, pos: *const f32, tgt: *const f32) -> *const c_void;

impl CamConfig {
    pub fn new() -> Self {
        CamConfig {
            cam_type: 3,
            Fov: 100.0,
            Near: 0.0,
            Pos_x: 0.0,
            Pos_y: 1.7762,
            Pos_z: 2.9527,
            // Tgt_x: 0.0,
            // Tgt_y: 1.5474,
            // Tgt_z: -2.9527,
        }
    }

    pub fn load_from_ini(&mut self, section: &ini::Properties) {
        self.Fov = section.get("Fov").unwrap().parse().unwrap();
        self.Near = section.get("Near").unwrap().parse().unwrap();
        self.Pos_x = section.get("Pos_x").unwrap().parse().unwrap();
        self.Pos_y = section.get("Pos_y").unwrap().parse().unwrap();
        self.Pos_z = section.get("Pos_z").unwrap().parse().unwrap();
        // self.Tgt_x = section.get("Tgt_x").unwrap().parse().unwrap();
        // self.Tgt_y = section.get("Tgt_y").unwrap().parse().unwrap();
        // self.Tgt_z = section.get("Tgt_z").unwrap().parse().unwrap();
    }

    pub fn read_from_memory(&mut self, _mem_helper: &mut crate::mem_helper::MemHelper) {
        // read from memory
        if !_mem_helper.open_process() {
            info!("Failed to open process.");
            return;
        }

        let p_rbr_carinfo = _mem_helper.read_pointer(0x0165FC68);
        if p_rbr_carinfo == 0 {
            info!("Failed to read carinfo pointer.");
            return;
        }

        let p_rbr_camera = _mem_helper.read_pointer(p_rbr_carinfo + 0x758);
        if p_rbr_camera == 0 {
            info!("Failed to read camera pointer.");
            return;
        }

        let p_rbr_camera_info = _mem_helper.read_pointer(p_rbr_camera + 0x10);
        if p_rbr_camera_info == 0 {
            info!("Failed to read camera info pointer.");
            return;
        }

        self.cam_type = _mem_helper.read_int(p_rbr_camera_info + 0x0);
        // self.Fov = _mem_helper.read_float(p_rbr_camera_info + 0x348);
        // self.Near = _mem_helper.read_float(p_rbr_camera_info + 0x34C);
        // force our fov and near
        self.Pos_x = _mem_helper.read_float(p_rbr_camera_info + 0x33C);
        self.Pos_z = _mem_helper.read_float(p_rbr_camera_info + 0x340);
        self.Pos_y = _mem_helper.read_float(p_rbr_camera_info + 0x344);
        // self.Tgt_x = _mem_helper.read_float(p_rbr_camera_info + 0x318);
        // self.Tgt_y = _mem_helper.read_float(p_rbr_camera_info + 0x31C);
        // self.Tgt_z = _mem_helper.read_float(p_rbr_camera_info + 0x320) 
    }

    pub fn write_to_memory(&self, _mem_helper: &mut crate::mem_helper::MemHelper) {
        // write to memory
        if !_mem_helper.open_process() {
            return;
        }
        let p_rbr_carinfo = _mem_helper.read_pointer(0x0165FC68);
        if p_rbr_carinfo == 0 {
            info!("Failed to read carinfo pointer.");
            return;
        }
        
        let p_rbr_camera = _mem_helper.read_pointer(p_rbr_carinfo + 0x758);
        if p_rbr_camera == 0 {
            info!("Failed to read camera pointer.");
            return;
        }

        let p_rbr_camera_info = _mem_helper.read_pointer(p_rbr_camera + 0x10);
        if p_rbr_camera_info == 0 {
            info!("Failed to read camera info pointer.");
            return;
        }

        _mem_helper.write_float(p_rbr_camera_info + 0x348, self.Fov);
        _mem_helper.write_float(p_rbr_camera_info + 0x34C, self.Near);
        _mem_helper.write_float(p_rbr_camera_info + 0x33C, self.Pos_x);
        _mem_helper.write_float(p_rbr_camera_info + 0x340, self.Pos_z);
        _mem_helper.write_float(p_rbr_camera_info + 0x344, self.Pos_y);
        info!("Wrote to memory: Fov: {}, Near: {}, Pos: ({}, {}, {})", self.Fov, self.Near, self.Pos_x, self.Pos_y, self.Pos_z);

        // nope, we don't write to memory, we call the function in the memory at 0x4663f0
        // which is `void* set_camera_target(void*, float*, float*)`
        // when changed the pos and target on imgui of Pacenote.dll, this method is called.
        // let func_addr = 0x4663f0;
        // let set_camera_func: SetCameraFunc = unsafe { std::mem::transmute(func_addr) };
        // let pos = [self.Pos_x, self.Pos_y, self.Pos_z];
        // let tgt = [self.Tgt_x, self.Tgt_y, self.Tgt_z];
        // let a: [u8; 4] = [0; 4];
        // info!("Calling set_camera_func at 0x{:X}", func_addr);

        // TODO: called but no effect. need to investigate how Pacenote.dll call this function.
        // unsafe { set_camera_func(a.as_ptr() as *const c_void, pos.as_ptr(), tgt.as_ptr()) };
    }
}

